#include "sc/setup.h"
#include "sc/interface.h"

#include <set>
#include <algorithm>

using namespace sc;

/**
* Setup Element constructor
*
* @param name    name of the Element
* @param content content of the Element
*/
Setup::Element::Element(const std::string& name, const std::string& content)
: m_sName(name)
, m_sContent(content)
{
}

/**
* Setup Element constructor
*
* @param name     name of the Element
* @param content  content of the Element
* @param iterCfg  iterator on the configuration of this Element
*/
Setup::Element::Element(const std::string& name, const std::string& content, Config::TMapElement::const_iterator iterCfg)
: m_sName(name)
, m_sContent(content)
, m_config(iterCfg)
{
    // Create all attributes
    Config::TListAttribut::const_iterator iterAtt;
    for (iterAtt = (*m_config).second.attributes.begin(); iterAtt != (*m_config).second.attributes.end(); iterAtt++)
    {
        Attribute attrib("", *iterAtt);
        m_attributes.insert( std::make_pair((*iterAtt).sName, attrib) );
    }
}

/**
* Setup Destructor
*/
Setup::Element::~Element()
{
}

/**
* Add a child to the element. The child is added only if his name is correct, and
*   if it doesn't appear as a child in case of OPTION or UNIQUE type
*
* @param rcElement   Element to add
* @param rIterChild [out] iterator on the added element
*
* @return error
*/
int Setup::Element::AddChild(const Element& rcElement, TListElement::iterator& rIterChild)
{
	int error = SCE_CHILD_INVALID;
	const Config::TListChild& availableChildren = (*m_config).second.children;
	Config::TListChild::const_iterator iterFound = std::find_if( availableChildren.begin()
															   , availableChildren.end()
															   , PredFindConfigChildName(rcElement.m_sName)
															   );
	if (iterFound != availableChildren.end())
	{
		// The name of the child is valid
		Element copy = *this;
		copy.m_children.push_back(rcElement);
		if (copy.IsValid())
		{
			// we can add the child
			m_children.push_back(rcElement);

			rIterChild = m_children.end();
			rIterChild--;

			// Add the UNIQUE children
			error = (*rIterChild).AddUniqueChildren();
		}
	}
	return error;
}

/**
* Add a child to the element. The child is added only if his name is correct, and
*   if it doesn't appear as a child in case of OPTION or UNIQUE type.
*
* @param rcElement   Element to add
*
* @return error
*/
int Setup::Element::AddChild(const Element& rcElement)
{
    TListElement::iterator iterChild;
    return AddChild(rcElement, iterChild);
}

/**
* Add all the UNIQUE children of the Element, according to the config
*
* @return error
*/
int Setup::Element::AddUniqueChildren(void)
{
    int error = SCE_NONE;

    // Add the UNIQUE elements
    Config::TListChild rcAvailableChildren = (*m_config).second.children;
    Config::TListChild::const_iterator iter;
    for (iter = rcAvailableChildren.begin(); iter != rcAvailableChildren.end(); iter++)
    {
        const Config::Element& availableChild = (*(*iter)).second;
        if ( availableChild.type == Config::Element::UNIQUE )
        {
            Element child(availableChild.sName, "", *iter);
            error = AddChild(child);
        }
    }
    return error;
}

/**
* Verify if an Element is valid, ie: it is correct in accordance with his config.
*
* @return true if the element is valid
*/
bool Setup::Element::IsValid() const
{
	bool bValid = (m_sName == (*m_config).first);

	// TODO : verifier les attributs

	std::set<std::string> childPresent;
	TListElement::const_iterator iterChild;
	for (iterChild = m_children.begin(); iterChild != m_children.end() && bValid; iterChild++)
	{
		Config::Element::Type type = (*((*iterChild).m_config)).second.type;
		if ((type == Config::Element::OPTION) || (type == Config::Element::UNIQUE))
		{
			// Test if there is no more than one element
			bValid &= childPresent.insert((*iterChild).m_sName).second;
		}
		bValid &= (*iterChild).IsValid();
	}
	return bValid;
}

/**
* Verify the child is available to be added to the current element
*
* @param sChildName  name of the child
*
* @return true if can add the child to the curren element
*/
bool Setup::Element::IsChildAvailable(const std::string& sChildName) const
{
	bool bAvailable = false;

	const Config::Element& rcConf = (*m_config).second;
	Config::TListChild::const_iterator iter;
	for (iter = rcConf.children.begin(); iter != rcConf.children.end(); iter++)
	{
		const Config::Element& rcChildConf = (*(*iter)).second;
		if (rcChildConf.sName == sChildName)
		{
			// The children is available
			bAvailable = true;

			if ((rcChildConf.type == Config::Element::UNIQUE) || (rcChildConf.type == Config::Element::OPTION))
			{
				TListElement::const_iterator iterFound = std::find_if( m_children.begin()
					                                                 , m_children.end()
																	 , PredFindChildName(sChildName)
																	 );
				if (iterFound != m_children.end())
				{
					// The children already exist, so it is not available
					bAvailable = false;
				}
			}
		}
	}
	return bAvailable;
}

/**
* Get all available children of an element
*
* @param rListChild [out]
*/
void Setup::Element::GetAvailableChildren(std::set<std::string>& rListChild) const
{
    const sc::Config::Element& rcConf = (*m_config).second;

    sc::Config::TListChild::const_iterator iterChild;
    for (iterChild = rcConf.children.begin(); iterChild != rcConf.children.end(); iterChild++)
    {
        if (IsChildAvailable( (*(*iterChild)).first ))
        {
            rListChild.insert( (*(*iterChild)).first );
        }
    }
}

/**
* Delete an element
*
* @param pcElement Element to delete
*
* @return delete error
*/
int Setup::Element::Delete(const Element* pcElement)
{
	int error = SCE_ELEMENT_DOESNT_EXIST;

	TListElement::iterator iter;
	for (iter = m_children.begin(); iter != m_children.end() && error != SCE_NONE; iter++)
	{
		if (&(*iter) == pcElement)
		{
			// We found the element
			m_children.erase(iter);
			error = SCE_NONE;
		}
		else
		{
			error = (*iter).Delete(pcElement);
		}
	}
	return error;
}

/**
* Save an element to an xml document
*
* @param xmlWriter  xml writer, ie : stream of the file
*
* @return saving error
*/
int Setup::Element::Save(xmlTextWriterPtr xmlWriter) const
{
	int error = SCE_CANT_SAVE_FILE;

	// Add the xml node with the name of the element
	if (xmlTextWriterStartElement(xmlWriter, (const xmlChar*) m_sName.c_str()) >= 0)
	{
		error = SCE_NONE;

		// Add the attributes
		TMapAttribut::const_iterator iterAtt;
		for (iterAtt = m_attributes.begin(); iterAtt != m_attributes.end() && !error; iterAtt++)
		{
			if ( xmlTextWriterWriteAttribute( xmlWriter
				                            , (const xmlChar*) (*iterAtt).first.c_str()
											, (const xmlChar*) (*iterAtt).second.m_sValue.c_str()
											) < 0 )
			{
				error = SCE_CANT_SAVE_FILE;
			}
		}

		// Add the children
		TListElement::const_iterator iterChild;
		for (iterChild = m_children.begin(); iterChild != m_children.end() && !error; iterChild++)
		{
			error = (*iterChild).Save(xmlWriter);
		}

		xmlTextWriterEndElement(xmlWriter);
	}
	return error;
}


/**
* Setup constructor
*/
Setup::Setup()
: m_pRoot(NULL)
{}

/**
* Setup destructor
*/
Setup::~Setup()
{
	Clear();
}

/**
* Clear the setup.
*  Remove all information about Setup
*/
void Setup::Clear(void)
{
    m_config.Clear();
    if (m_pRoot)
    {
        delete m_pRoot;
        m_pRoot = NULL;
    }
}

/**
* Load a config file, ie: a rng file which describe the capacity of of the setup
*
* @param sConfigFilename File name of the config file (RNG file)
*
* @return error
*/
int Setup::LoadConfig(const std::string& sConfigFilename)
{
	Clear();

	// load config
	int error = m_config.LoadRng(sConfigFilename);

	if (!error)
	{
		// Add the root to the setup, in accordance with the config loaded
		std::string sRoot;
		error = m_config.GetRoot(sRoot);
		if (!error)
		{
			m_pRoot = new Element(sRoot, "", m_config.GetElements().find(sRoot));
			if (!m_pRoot || !m_pRoot->IsValid())
			{
				error = SCE_UNKNOWN;
			}
			else
			{
				// Add the UNIQUE elements
				m_pRoot->AddUniqueChildren();
            }
		}
	}
	return error;
}

/**
* Load the setup
*
* @param sFilename
*
* @return loading error
*/
int Setup::Load(const std::string& sFilename)
{
    int error = SCE_SHEMA_INVALID;

    error = SCE_NONE;

    return error;
}

/**
* Delete an element
*
* @param pcElement Element to delete
*
* @return delete error
*/
int Setup::DeleteElement(const Element* pcElement)
{
	if (m_pRoot == NULL)
		return SCE_RNG_NOT_LOADED;

	return m_pRoot->Delete(pcElement);
}

/**
* Save the setup as a xml file, with the grammar defined corresponding to the config (RNG) file
*
* @param sFilename  file name of the file where the setup will be saved
*
* @return saving error
*/
int Setup::Save(const std::string& sFilename) const
{
	if (m_pRoot == NULL)
		return SCE_RNG_NOT_LOADED;

	int error = SCE_CANT_SAVE_FILE;

	xmlTextWriterPtr xmlWriter = xmlNewTextWriterFilename(sFilename.c_str(), 0);
	if (xmlWriter && !xmlTextWriterSetIndent(xmlWriter, 2))
	{
		if (xmlTextWriterStartDocument(xmlWriter, NULL, NULL, NULL) >= 0)
		{
			error = m_pRoot->Save(xmlWriter);
			if (!error)
			{
				if (xmlTextWriterEndDocument(xmlWriter) < 0)
				{
					error = SCE_CANT_SAVE_FILE;
				}
			}
		}
		xmlFreeTextWriter(xmlWriter);
	}
	return error;
}
